# Unit tests and integration tests for Pktgen-DPDK
#
# The test harness used by the unit tests is libtap, obtained from
# <https://github.com/zorgnax/libtap>.


### Environment checks
ifeq ($(RTE_SDK),)
$(error "Please define RTE_SDK environment variable")
endif

ifeq ($(wildcard ../build/app/pktgen),)
$(error "Please compile Pktgen first")
endif

ifeq ($(RTE_TARGET),)
RTE_TARGET := x86_64-wr-linuxapp-gcc
$(warning "RTE_TARGET is not defined, using default value $(RTE_TARGET)")
endif

# build/ holds the .o files from our tests.
# build/app/ holds the .o files from the Pktgen C files needed by the tests.
# They go in a separate location, so they can be easily distinguished and get
# a separate compile command (for example to enable code coverage functions).
$(shell mkdir -p build/app/)

# lcov/ holds the coverage data, lcov/html/ contains the HTML coverage report.
$(shell mkdir -p lcov/html)


### Use DPDK CC, CFLAGS, ...
include $(RTE_SDK)/mk/rte.vars.mk


### Extra flags for test compilation and linking
TEST_CFLAGS  := -DTESTING -include libtap/tap.h -include helpers/test_helpers.h -I.. -Wall -Werror
TEST_LDFLAGS := -Wall -Werror

### Extra flags for application source files
# Optimization is disabled and code coverage instrumentation is enabled. This
# allows for test coverage reports.
APP_CFLAGS := -O0 --coverage -g3 -Wall -DTESTING
# Move application main() out of the way, so it doesn't conflict with the main()
# from the unit test.
APP_CFLAGS += -Dmain="ORIG_MAIN"

APP_LDFLAGS := -O0 --coverage


### verbose/quiet options for various commands, so requested verbosity is
# respected.
MK_PROVE_OPTS=--QUIET
MK_LCOV_OPTS=--quiet
ifdef V
ifeq ("$(origin V)", "command line")
MK_PROVE_OPTS=-v
MK_LCOV_OPTS=
endif
endif

PROVE_OPTS+=$(MK_PROVE_OPTS)
LCOV_OPTS+=$(MK_LCOV_OPTS)


### Tests to build/run.
# Tests can be either C files (code unit tests) or Perl scripts (functionality
# and integration testing).
C_TESTS  := $(patsubst %.c, %, $(wildcard *.t.c))
PL_TESTS := $(wildcard *.pl.t)

# Sort the filenames, so execution order is maintained.
ALL_TESTS := $(sort $(C_TESTS) $(PL_TESTS))


### Make targets
.PHONY: all tests check test clean

all: libtap/libtap.a tests

tests: $(ALL_TESTS) lcov/coverage-base.info


# Prepend ./ to test filenames. If . is not in $PATH, prove will fail otherwise.
check test: all
	@echo "  RUN-TESTS $(ALL_TESTS)"
	$(Q)prove $(PROVE_OPTS) $(ALL_TESTS:%=./%)

# Generate test coverage report in HTML
cover: $(C_TESTS) lcov/coverage-base.info
	$(Q)cp lcov/coverage-base.info lcov/coverage-total.info

	$(Q)for test in $(C_TESTS); do                                             \
		echo "  RUN-TEST  $${test}";                                           \
		./$${test} >/dev/null;                                                 \
	done

	@echo "  COVER     parse data"
	$(Q)lcov $(LCOV_OPTS) -c --base-directory ./ --derive-func-data -d build/app/ -o lcov/coverage-test.info
	$(Q)lcov $(LCOV_OPTS) --base-directory ./ --derive-func-data -o lcov/coverage-total.info -a lcov/coverage-base.info -a lcov/coverage-test.info
	$(Q)lcov $(LCOV_OPTS) -o lcov/coverage-filtered.info -e lcov/coverage-total.info "$$RTE_SDK/wr-examples/pktgen/*"

	@echo "  HTML      `pwd`/lcov/html/index.html"
	$(Q)genhtml $(LCOV_OPTS) -o lcov/html/ -s --legend lcov/coverage-filtered.info


clean:
	@echo "  CLEAN"
	$(Q)rm -rf *.o libtap/*.o libtap/libtap.a build/ lcov/ $(C_TESTS)

# Test dependencies. The dependency files contain make targets, so they must be
# included after the default make target (all) is set.
-include $(C_TESTS:%=build/%.d)

build/%.d: %.c helpers/makedeps
	@echo "  DEPS      $<"
	$(Q)helpers/makedeps $< $@



### File recipes
## libtap library
libtap/tap.o: libtap/tap.c
	@echo "  TAP-CC    $@"
	$(Q)$(CC) $(CFLAGS) -c $< -o $@

libtap/libtap.a: libtap/tap.o
	@echo "  TAP-AR    $@"
	$(Q)$(AR) rcs $@ $(filter %.o, $^)

## Test object files
# $($@_CFLAGS) are extra CFLAGS for a specific test .o file. These CFLAGS are
# generated by the makedeps script and contain "-include ../<foo>.h" for every
# SOURCE entry <foo> specified in the test where ../<foo.h> exists.
$(patsubst %, build/%.o, $(C_TESTS)): build/%.o: %.c libtap/tap.h
	@echo "  CC        $@"
	$(Q)$(CC) $(CFLAGS) $(TEST_CFLAGS) $($@_CFLAGS) -c $< -o $@

## Application object files
build/app/%.o: ../%.c
	@echo "  CC        $@"
	$(Q)$(CC) $(CFLAGS) $(APP_CFLAGS) -c $< -o $@

## Function stubs
$(patsubst %, build/%.stubs.o, $(C_TESTS)): build/%.stubs.o: build/%.stubs.c libtap/tap.h
	@echo "  CC        $@"
	$(Q)$(CC) $(CFLAGS) $(TEST_CFLAGS) -c $< -o $@

$(patsubst %, build/%.stubs.c, $(C_TESTS)): build/%.stubs.c: %.c helpers/genstubs
	@echo "  GEN-STUBS $<"
	$(Q)helpers/genstubs $< $@

$(patsubst %, build/%.h, $(C_TESTS)): build/%.h: %.c helpers/genstubs
	@echo "  GEN-HDR   $<"
	$(Q)helpers/genstubs $< $@

$(patsubst %, build/%.fake_impl.o, $(C_TESTS)): helpers/fake_impl.c
	@echo "  CC        $@"
	$(Q)$(CC) $(CFLAGS) $(TEST_CFLAGS) $($@_CFLAGS) -c $< -o $@

## Test executables
# $($@_LDFLAGS) may contain system libraries to link against. See the
# explanation of $($@_CFLAGS) under "Test object files" for details.
$(C_TESTS): %: build/%.o build/%.stubs.o build/%.fake_impl.o libtap/libtap.a
	@echo "  LD        $@"
	$(Q)$(CC) $(LDFLAGS) $(TEST_LDFLAGS) $(APP_LDFLAGS) $^ $($@_LDFLAGS) -o $@

## Coverage files
lcov/coverage-base.info: $(C_TESTS)
	@echo "  COVER     $@"
	$(Q)lcov $(LCOV_OPTS) -c -i --base-directory ./ -d build/app/ -o $@
